﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Web.Mvc;
using Inscriptionem.Areas.Inscriptionem.Models.Enums;
using Inscriptionem.Areas.Inscriptionem.Models.Requests;
using Inscriptionem.Areas.Inscriptionem.Models.Views;
using Inscriptionem.Authorization;
using Inscriptionem.Controllers;
using Inscriptionem.Extensions;

namespace Inscriptionem.Areas.Inscriptionem.Controllers
{
    [InscriptionemAuthorize(MemberRole.Admin, MemberRole.Moderator, MemberRole.Writer)]
    public class PageController : Controller
    {
        private readonly ORM.DB DB;
        public PageController()
        {
            DB = new ORM.DB();
        }

        public ActionResult Index()
        {
            return View("Index", new PageCollectionViewModel());
        }

        public ActionResult Add()
        {
            return View("Add", new PageViewModel() { IsCommentable = true, IsPinned = false, IsPublished = false, CreatedDate = DateTime.Now });
        }

        [HttpPost]
        [ValidateInput(false)]
        public ActionResult Add(PageRequestModel model)
        {
            var result = new PageViewModel();
            model.CopyTo(result);

            result.Error = ValidatePageAddModel(result);

            if (result.Error == PageError.None)
            {
                try
                {
                    DateTime date = DateTime.Now;
                    var section = DB.Sections.First(s => s.ID == result.Section);

                    var page = new ORM.Page()
                                   {
                                       Title = result.Title ?? string.Empty,
                                       Content = result.Content ?? string.Empty,
                                       IsCommentable = result.IsCommentable,
                                       IsPublished = result.IsPublished,
                                       IsSectionPinned = result.IsPinned,
                                       Section = section,
                                       Member = DB.Members.First(m => m.ID == Member.Current.ID),
                                       Order = result.Order,
                                       ViewCount = 0,
                                       CreatedDate = result.CreatedDate.HasValue ? result.CreatedDate.Value : date,
                                       ModifiedDate = date
                                   };

                    DB.AddToPages(page);

                    // Tags
                    var tags = (result.Tags ?? string.Empty).Split(new char[] {','}, StringSplitOptions.RemoveEmptyEntries).Select(t => t.Trim()).Distinct().ToList();

                    for (int i = 0; i < tags.Count; i++)
                    {
                        string currentTag = tags[i];
                        var tag = DB.Tags.FirstOrDefault(t => t.Name.ToLower() == currentTag.ToLower() && t.Language.ID == section.Language.ID);

                        if (tag == null)
                        {
                            tag = new ORM.Tag()
                                      {
                                          Name = currentTag,
                                          Language = section.Language
                                      };

                            DB.AddToTags(tag);
                        }

                        var pageTag = new ORM.PageTags()
                                          {
                                              Tag = tag,
                                              Page = page
                                          };

                        DB.AddToPageTags(pageTag);
                    }

                    DB.SaveChanges();
                }
                catch (Exception)
                {
                    result.Error = PageError.Other;
                }
            }

            return View("Add", result);
        }

        [HttpPost]
        [ValidateInput(false)]
        public ActionResult Edit(PageRequestModel model)
        {
            var result = new PageViewModel();
            model.CopyTo(result);

            // Not the best way to check if "Edit" button was pressed
            if (result.ID > 0 && string.IsNullOrWhiteSpace(result.Title) && string.IsNullOrWhiteSpace(result.Content) && string.IsNullOrWhiteSpace(result.Tags) && result.Section == 0)
            {
                var page = DB.Pages.FirstOrDefault(p => p.ID == result.ID);
                if (page == null)
                {
                    // Page not found
                    return RedirectToAction("Index");
                }

                result.Title = page.Title;
                result.Content = page.Content;
                result.Section = page.Section.ID;
                result.IsCommentable = page.IsCommentable;
                result.IsPinned = page.IsSectionPinned;
                result.IsPublished = page.IsPublished;
                result.CreatedDate = page.CreatedDate;
                result.Order = page.Order;
                result.Tags = string.Join(", ", page.PageTags.Select(pt => pt.Tag).Select(t => t.Name));
                result.Member = page.Member;

                return View("Edit", result);
            }

            // Validate
            result.Error = ValidatePageEditModel(model);
            if (result.Error == PageError.None)
            {
                // Save changes
                try
                {
                    var page = DB.Pages.FirstOrDefault(p => p.ID == result.ID);

                    if (page == null)
                    {
                        return RedirectToAction("Index");
                    }

                    var section = DB.Sections.First(s => s.ID == result.Section);

                    page.Title = result.Title ?? string.Empty;
                    page.Content = result.Content ?? string.Empty;
                    page.Section = section;
                    page.IsCommentable = result.IsCommentable;
                    page.IsSectionPinned = result.IsPinned;
                    page.IsPublished = result.IsPublished;
                    page.CreatedDate = result.CreatedDate.HasValue ? result.CreatedDate.Value : DateTime.Now;
                    page.Order = result.Order;
                    page.Member = DB.Members.First(m => m.ID == result.MemberID);
                    page.ModifiedDate = DateTime.Now;

                    // Tags
                    var tags = (result.Tags ?? string.Empty).Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries).Select(t => t.Trim()).Distinct().ToList();

                    List<ORM.PageTags> unusedPageTags = page.PageTags.ToList();

                    for (int i = 0; i < tags.Count; i++)
                    {
                        string currentTag = tags[i];
                        var tag = DB.Tags.FirstOrDefault(t => t.Name.ToLower() == currentTag.ToLower() && t.Language.ID == section.Language.ID);

                        if (tag == null)
                        {
                            tag = new ORM.Tag()
                            {
                                Name = currentTag,
                                Language = section.Language
                            };

                            DB.AddToTags(tag);
                        }

                        var pageTag = DB.PageTags.FirstOrDefault(pt => pt.Tag.ID == tag.ID && pt.Page.ID == page.ID);
                        if (pageTag == null)
                        {
                            pageTag = new ORM.PageTags()
                            {
                                Tag = tag,
                                Page = page
                            };
                        }
                        else
                        {
                            unusedPageTags.Remove(pageTag);
                        }

                        DB.AddToPageTags(pageTag);
                    }

                    unusedPageTags.ForEach(pt => DB.PageTags.DeleteObject(pt));

                    DB.SaveChanges();

                    return RedirectToAction("Index");
                }
                catch (Exception)
                {
                    result.Error = PageError.Other;
                }
            }

            return View("Edit", result);
        }

        [HttpPost]
        public ActionResult Delete(int ID)
        {
            if (ID <= 0)
            {
                return RedirectToAction("Index");
            }

            var page = DB.Pages.FirstOrDefault(p => p.ID == ID);
            if (page == null)
            {
                return RedirectToAction("Index");
            }

            PageError result = PageError.None;

            try
            {
                if (page.Comments.Any())
                {
                    result = PageError.PageHasContent;
                }
                else
                {
                    var pageTags = DB.PageTags.Where(pt => pt.Page.ID == page.ID);
                    foreach (var pageTag in pageTags)
                    {
                        DB.PageTags.DeleteObject(pageTag);
                    }

                    DB.Pages.DeleteObject(page);
                    DB.SaveChanges();

                    return RedirectToAction("Index");
                }
            }
            catch (Exception)
            {
                result = PageError.Other;
            }

            return View("Delete", result);
        }

        [HttpPost]
        public ActionResult Search(string Text, int Page)
        {
            if (Page < 0)
            {
                Page = 0;
            }

            var result = new PageSearchCollectionViewModel { Page = Page, Text = Text };

            return View("Search", result);
        }

        public ActionResult Page(int ID)
        {
            if (ID <= 0)
            {
                return RedirectToAction("Index");
            }

            return View("Index", new PageCollectionViewModel() { Page = ID });
        }

        public ActionResult Order()
        {
            return View("Order", new PageCollectionViewModel());
        }

        [HttpPost]
        public ActionResult Order(IList<PageOrderRequestModel> pages)
        {
            var error = ValidatePageOrderModel(pages);
            if (error == OrderError.None)
            {
                // Save
                foreach (var page in pages)
                {
                    var dbPage = DB.Pages.FirstOrDefault(p => p.ID == page.ID);
                    if (dbPage != null)
                    {
                        dbPage.Order = page.Order;
                        dbPage.ModifiedDate = DateTime.Now;
                    }
                }

                DB.SaveChanges();
            }

            return View("Order", new PageCollectionViewModel() { Error = error });
        }

        #region Pagination
        public ActionResult Pagination(global::Inscriptionem.Models.Menu.Requests.PaginationRequestModel model)
        {
            return new MenuController().Pagination(model);
        }
        #endregion

        #region Implementation
        private OrderError ValidatePageOrderModel(IList<PageOrderRequestModel> Pages)
        {
            if (Pages == null || Pages.Count == 0)
            {
                return OrderError.Other;
            }

            if (Pages.Any(p => p.Order <= 0))
            {
                return OrderError.OrderNotValid;
            }

            var lookup = Pages.ToLookup(p => p.Section);

            foreach (var group in lookup)
            {
                var list = new List<int>();

                foreach (var page in group)
                {
                    if (!list.Contains(page.Order))
                    {
                        list.Add(page.Order);
                    }
                    else
                    {
                        return OrderError.OrderDuplicate;
                    }
                }
            }

            return OrderError.None;
        }

        private PageError ValidatePageAddModel(PageRequestModel Model)
        {
            var editResult = ValidatePageEditModel(Model);
            if (editResult != PageError.None)
            {
                return editResult;
            }

            return PageError.None;
        }

        private PageError ValidatePageEditModel(PageRequestModel Model)
        {
            if (Model.Order <= 0)
            {
                return PageError.OrderNotValid;
            }

            var section = DB.Sections.FirstOrDefault(s => s.ID == Model.Section);
            if(section == null)
            {
                return PageError.PageSectionEmpty;
            }

            var page = section.Pages.FirstOrDefault(p => p.Order == Model.Order);
            if (page != null && page.ID != Model.ID)
            {
                return PageError.OrderAlreadyInUse;
            }

            return PageError.None;
        }
        #endregion
    }
}
